﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace ManagedScenarios
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("PowerDbg Managed Scenarios");
            Console.WriteLine("Running under CLR {0} ({1})", 
                Environment.Version,
                IntPtr.Size == 4 ? "32bit" : "64bit"
                );
            Console.WriteLine();

            // Scenario not selected: provide choice
            string[] scenarioArgs;
            Type scenarioType;
            if (args.Length == 0)
            {
                scenarioType = SelectScenarioFromMenu();
                if(scenarioType==null)
                    return;

                scenarioArgs = args;
            }
            else
            {
                var scenarioName = args[0];
                if (!scenarioName.Contains("."))
                    scenarioName = typeof(Program).Namespace + "." + scenarioName;
                scenarioType = Type.GetType(scenarioName, true, true);

                // Pass all the same args to the scenario
                // *except* the first one (the scenario name)
                scenarioArgs = new string[args.Length-1];
                Array.Copy(args, 1, scenarioArgs, 0, scenarioArgs.Length);
            }

            // Spin the scenario up dynamically based on the command line argument
            // and execute it
            var scenario = (IScenario)Activator.CreateInstance(scenarioType);
            scenario.Run(scenarioArgs);

            GC.KeepAlive(scenario); // let's not have any wierd 'being GC'd whilst being executed' behavior, shall we
        }

        private static Type SelectScenarioFromMenu()
        {
            // For the menu, display a list of all public IScenarios in the assembly
            // (internal ones can still be called explicitly, but will not be shown here)

            var scenarios = typeof(Program).Assembly.GetTypes()
                .Where(t => !t.IsAbstract && typeof(IScenario).IsAssignableFrom(t) && t.IsPublic)
                .OrderBy(t => t.Name)
                .Select(t => new
                                 {
                                     Type = t,
                                     Description = GetTypeDescription(t)
                                 })
                ;

                
            var map = new Dictionary<int, Type>();
            var index = 1;
            foreach (var scenarioDetails in scenarios)
            {
                map.Add(index, scenarioDetails.Type);
                Console.WriteLine("{0,3}: {1}", index, scenarioDetails.Type.Name);

                var descriptionAttribute = scenarioDetails.Description;
                if(descriptionAttribute!=null)
                    Console.WriteLine("     {0}", descriptionAttribute);

                index++;
            }

            int selected = 0;
            do
            {
                var entered = Console.ReadLine();
                if (string.IsNullOrEmpty(entered))
                    return null;

                if (!int.TryParse(entered, out selected) || !map.ContainsKey(selected))
                    Console.WriteLine("Invalid entry - pick a number from the list (CTRL-C exits)");

            } while (selected == 0);

            // valid scenario selected
            return map[selected];
        }

        private static string GetTypeDescription(Type t)
        {
            return ((DescriptionAttribute[])t.GetCustomAttributes(typeof(DescriptionAttribute), false))
                .Select(d => d.Description)
                .FirstOrDefault();
        }
    }
}
